// Go作者版权所有。版权所有。
// 此源代码的使用受BSD样式的约束
// 可以在许可证文件中找到的许可证。

// Test2json将go测试输出转换为机器可读的JSON流。
// None
// 用法：
// None
// go工具test2json[-p pkg][-t][./pkg.test-test.v[-test.paniconexit0]]
// None
// Test2json运行给定的测试命令并将其输出转换为JSON；
// 在没有指定命令的情况下，test2json期望标准输入上的测试输出。
// 它将相应的JSON事件流写入标准输出。
// 没有不必要的输入或输出缓冲，因此
// 可以读取JSON流以获取测试状态的“实时更新”。
// None
// p标志设置每个测试事件中报告的包。
// None
// -t标志要求向每个测试事件添加时间戳。
// None
// 必须使用-test.v调用该测试。额外通过
// -test.paniconexit0将导致test2json以非零值退出
// 正在运行的测试之一调用os.Exit（0）时的状态。
// None
// 请注意，test2json仅用于转换单个测试
// 二进制文件的输出。要转换“go test”命令的输出，
// 使用“GoTest-json”而不是直接调用test2json。
// None
// 输出格式
// None
// JSON流是由TestEvent对象的换行分隔序列
// 对应于Go结构：
// None
// 类型TestEvent结构{
// 时间，时间
// 动作串
// 包串
// 测试字符串
// 已用浮点64
// 输出字符串
// }
// None
// 时间字段保存事件发生的时间。
// 对于缓存的测试结果，通常省略它。
// None
// “操作”字段是一组固定的操作描述之一：
// None
// 运行-测试已开始运行
// 暂停-测试已暂停
// cont-测试已继续运行
// 通过-测试通过
// 工作台-基准打印日志输出，但未失败
// 失败-测试或基准测试失败
// 输出-测试打印输出
// 跳过-已跳过测试或包中不包含任何测试
// None
// “包”字段（如果存在）指定要测试的包。
// 当go命令在-json模式下运行并行测试时，来自
// 不同的测试是交错的；Package字段允许读卡器
// 把它们分开。
// None
// 测试字段（如果存在）指定测试、示例或基准
// 导致事件的函数。整个包测试的事件
// 不要设置测试。
// None
// 已用时间字段设置为“通过”和“失败”事件。它给了我们时间
// 通过或失败的特定测试或整个程序包测试的运行时间。
// None
// 输出字段设置为Action==“Output”，是测试输出的一部分
// （标准输出和标准误差合并在一起）。输出是
// 未修改，但强制测试的无效UTF-8输出除外
// 通过使用替换字符转换为有效的UTF-8。除此之外,，
// 所有输出事件的输出字段的串联是精确的
// 测试执行的输出。
// None
// 当基准测试运行时，它通常只生成一行输出
// 给出计时结果。在Action==“output”的事件中报告该行
// 没有试验场。如果基准记录输出或报告故障
// （例如，通过使用b.Log或b.Error）报告额外的输出
// 作为测试集为基准名称的事件序列，终止
// 最后一个事件的动作==“基准”或“失败”。
// 基准测试没有Action==“run”、“pause”或“cont”的事件。
// None
package main

import (
	"flag"
	"fmt"
	exec "internal/execabs"
	"io"
	"os"

	"cmd/internal/test2json"
)

var (
	flagP = flag.String("p", "", "report `pkg` as the package being tested in each event")
	flagT = flag.Bool("t", false, "include timestamps in events")
)

func usage() {
	fmt.Fprintf(os.Stderr, "usage: go tool test2json [-p pkg] [-t] [./pkg.test -test.v]\n")
	os.Exit(2)
}

func main() {
	flag.Usage = usage
	flag.Parse()

	var mode test2json.Mode
	if *flagT {
		mode |= test2json.Timestamp
	}
	c := test2json.NewConverter(os.Stdout, *flagP, mode)
	defer c.Close()

	if flag.NArg() == 0 {
		io.Copy(c, os.Stdin)
	} else {
		args := flag.Args()
		cmd := exec.Command(args[0], args[1:]...)
		w := &countWriter{0, c}
		cmd.Stdout = w
		cmd.Stderr = w
		err := cmd.Run()
		if err != nil {
			if w.n > 0 {
				// 假设命令打印了失败的原因。
			} else {
				fmt.Fprintf(c, "test2json: %v\n", err)
			}
		}
		c.Exited(err)
		if err != nil {
			c.Close()
			os.Exit(1)
		}
	}
}

type countWriter struct {
	n int64
	w io.Writer
}

func (w *countWriter) Write(b []byte) (int, error) {
	w.n += int64(len(b))
	return w.w.Write(b)
}
